import sys
import time
from collections import OrderedDict
from PyMimircache import Cachecow
from PyMimircache.cache.lru import LRU
from PyMimircache.cacheReader.requestItem import Req

c=Cachecow()

#通过运行三层存储层次，获得底层存储设备的访问百分比
#运行此脚本时格式为 'python 脚本名 trace路径'
trace_path=sys.argv[1]
reader=c.open(trace_path)

"""
1. 定义DRAM、NVM、nvm参数：
            容量    速度
    DRAM    16G      80ns
    NVM     128G    300ns
    SSD     512G    150us
"""
# SSD
ssd_size=reader.get_num_of_uniq_req()
ssd_speed=1875
ssd_miss_path="traces/SSD_miss.txt"
ssd_hit_num=0
# NVM
nvm_size=int(0.25*reader.get_num_of_uniq_req())
nvm_speed=4
nvm_miss_path="traces/NVM_miss.txt"
nvm_hit_num=0
nvm_miss_num=0
nvm_access_num=0
# DRAM
dram_size=int(0.03*reader.get_num_of_uniq_req())
if dram_size <1:
    dram_size=1
dram_speed=1
dram_miss_path="traces/DRAM_miss.txt"
dram_hit_num=0
dram_miss_num=0
dram_access_num=0

"""
2. 定义back_end的ssd，并装入所有的trace数据
"""
# 定义back_end的ssd，其大小能装下所有uniq trace
ssd_cache=LRU(cache_size=ssd_size)
print("ssd size is ",ssd_cache.cache_size)
# 在back_end的ssd中装入全部数据
n_req=reader.get_num_of_req()
evict_item_list = []
for n in range(n_req):
    request=reader.read_one_req()
    req=Req(request)

    if ssd_cache.access(req):
        continue

print("back end ssd_cache info-------------------------------------------")
print(ssd_cache)
print("back end ssd_cache info-------------------------------------------")
reader.reset()


"""
3. 定义2层基于ssd的cache：dram-nvm-ssd
    DRAM接受原始trace，后两层均为前一层miss的数据
"""
# 定义函数实现不同cache的配置
def layer_cache(lru_cache,speed,trace_path,miss_path):
    # 以追加写的方式打开miss文件
    miss_file=open(miss_path, mode='a+')
    
    reader=c.open(trace_path)

    # 获取trace中的行数
    n_req=reader.get_num_of_req()

    # 定义访问次数
    hit_number=0
    miss_number=0

    # 遍历每一行trace数据
    for n in range(n_req):
        request=reader.read_one_req()
        req=Req(request)
        # print("req's id is",req.item_id)

        # 对应层次的速度进行延迟
        for i in range(speed):
            continue

        if lru_cache.access(req):
            hit_number=hit_number+1
            continue
        # 记录导致cache miss
        miss_file.write(req.item_id)
        miss_file.write('\n')

    # 输入的trace中的数据，要么hit，要么miss，所以miss次数为总次数-hit次数
    print("n_req is ",n_req)
    print("hit_number is ",hit_number)
    miss_number=n_req-hit_number
    print("miss_number is ",miss_number)

    miss_file.close()

    return hit_number,miss_number

# 计算三层运行的时间
start = time.time()
# 定义每层的访问次数=hit次数+miss次数
access_num=0
# 定义三层结构DRAM-nvm-ssd，原始trace依次经过过滤，正确时ssd_miss_file中应该为空
n_layer=0
for n_layer in range(3):
    if n_layer==0:
        # 定义DRAM
        dram_cache=LRU(cache_size=dram_size)
        print("dram cache size is",dram_cache.cache_size)
        # 向dram输入原始trace,并获得dram层的访问次数
        dram_hit_num,dram_miss_num=layer_cache(dram_cache,dram_speed,trace_path,dram_miss_path)
        # 计算在dram层处理数据的时间
        dram_end=time.time()
        print("dram time is",dram_end-start)
        #获得dram层的访问次数(hit+miss)，其中dram_miss_num由于下层会promote数据所以需要乘2
        dram_miss_num=dram_miss_num*2
        dram_access_num=dram_hit_num+dram_miss_num
        print("dram hit number is",dram_hit_num)
        print("dram miss number is",dram_miss_num)
        print("_____________________________________________________")
        continue

    elif n_layer==1:
        # 定义nvm
        nvm_cache=LRU(cache_size=nvm_size)
        print("nvm cache size is",nvm_cache.cache_size)
        # 向nvm输入dram层miss的trace,并获得nvm层的访问次数
        nvm_hit_num,nvm_miss_num=layer_cache(nvm_cache,nvm_speed,dram_miss_path,nvm_miss_path)
        # 计算在nvm层处理数据的时间
        nvm_end=time.time()
        print("nvm time is",nvm_end-dram_end)
        #获得nvm层的访问次数，其中nvm_miss_num由于下层会promote数据所以需要乘2
        nvm_miss_num=nvm_miss_num*2
        nvm_access_num=nvm_hit_num+nvm_miss_num
        print("nvm hit number is",nvm_hit_num)
        print("nvm miss number is",nvm_miss_num)
        print("_____________________________________________________")
        continue

    else:
        # 定义ssd
        print("enter in ssd")
        # 向ssd输入nvm层miss的trace,并获得ssd层的访问次数
        ssd_hit_num,ssd_miss_num=layer_cache(ssd_cache,ssd_speed,nvm_miss_path,ssd_miss_path)
        # 计算在ssd层处理数据的时间
        ssd_end=time.time()
        print("ssd time is",ssd_end-nvm_end)
        #获得ssd层的访问次数，其中ssd_miss_num正确时应为0
        print("ssd hit number is",ssd_hit_num)
        print("ssd miss number is",ssd_miss_num)


end = time.time()
print("three cache's time is",end-start)

access_num=dram_access_num+nvm_access_num+ssd_hit_num
print("dram access proportion is ",dram_access_num/access_num)
print("nvm access proportion is ",nvm_access_num/access_num)
print("ssd access proportion is ",ssd_hit_num/access_num)





